#! /usr/bin/perl -w
#-------------------------------------------------------------------------
#
# Gen_fmgrtab.pl
#    Perl script that generates fmgroids.h and fmgrtab.c from pg_builtin_proc.cpp
#
# Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
# Portions Copyright (c) 1994, Regents of the University of California
#
#
# IDENTIFICATION
#    src/backend/utils/Gen_fmgrtab.pl
#
#-------------------------------------------------------------------------
use strict;
use warnings;

# Collect arguments
my @infile;    # builtin_funcs.ini
my $output_path = '';
my %catalog;
$catalog{builtindata} = [];
my $nfuncs = 0;
my $nfmgrfuncs = 0;

while (@ARGV)
{
	my $arg = shift @ARGV;
	if ($arg !~ /^-/)
	{
		push @infile, $arg;
	}
	elsif ($arg =~ /^-o/)
	{
		$output_path = length($arg) > 2 ? substr($arg, 2) : shift @ARGV;
	}
	else
	{
		usage();
	}
}

# Make sure output_path ends in a slash.
if ($output_path ne '' && substr($output_path, -1) ne '/')
{
	$output_path .= '/';
}
foreach my $singlefile (@infile)
{
	open(INPUT_FILE, '<', $singlefile) || die "$singlefile: $!";

	while (<INPUT_FILE>)
	{
		s;/\*(.|\n)*\*/;;g;
		if (m;/\*;)
		{
			# handle multi-line comments properly.
			my $next_line = <INPUT_FILE>;
			die "$singlefile: ends within C-style comment\n"
				if !defined $next_line;
			$_ .= $next_line;
			redo;
		}
		# Strip useless whitespace and trailing semicolons.
		chomp;
		s/^\s+//;
		s/;\s*$//;
		s/\s+/ /g;
		if (/AddBuiltinFunc\((.+?)\)\)/)
		{
			push @{ $catalog{builtindata} }, $1;
			$nfuncs = $nfuncs + 1;	
		}
	}
}

#parse all builtin functions
my @fmgr = ();
my $foid;     #[0]
my $funcName; #[1]
my $nargs;   #[2]
my $strict;  #[3]
my $retset;  #[4]
my $prosrc;  #[25]
my $prorettype; #[6]
		
foreach my $row (@{ $catalog{builtindata} })
{
	if ($row =~ /_0\(([0-9A-Z]+)\),\s+_1\(\"(\S+)\"\),\s+_2\((\d+)\),\s+_3\((\w+)\),\s+_4\((\w+)\),\s+.+?_6\((\d+)\),.+?_25\(\"(\w+)\"\),/)
	{
		$foid = $1;
		$funcName = $2;
		$nargs = $3;
		$strict = $4;
		$retset = $5;
		$prosrc = $7;
		$prorettype = $6;
		
		push @fmgr,
	  { oid    => $foid,
	  	proname =>$funcName,
		strict => $strict,
		retset => $retset,
		nargs  => $nargs,
		prosrc => $prosrc, 
		prorettype => $prorettype
	  };
	  
	}
	
	# Hack to work around memory leak in some versions of Perl
	$row = undef;
}
# Emit headers for both files
my $tmpext   = ".tmp$$";
my $oidsfile = $output_path . 'fmgroids.h';
my $tabfile  = $output_path . 'fmgrtab.cpp';
my $builtinfile = $output_path . 'pg_builtin_proc.h';

open H, '>', $oidsfile . $tmpext or die "Could not open $oidsfile$tmpext: $!";
open T, '>', $tabfile . $tmpext  or die "Could not open $tabfile$tmpext: $!";
open B, '>', $builtinfile . $tmpext  or die "Could not open $builtinfile$tmpext: $!";

print H
qq|/*-------------------------------------------------------------------------
 *
 * fmgroids.h
 *    Macros that define the OIDs of built-in functions.
 *
 * These macros can be used to avoid a catalog lookup when a specific
 * fmgr-callable function needs to be referenced.
 *
 * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * NOTES
 *	******************************
 *	*** DO NOT EDIT THIS FILE! ***
 *	******************************
 *
 *	It has been GENERATED by $0
 *	from $infile[0]
 *
 *-------------------------------------------------------------------------
 */
#ifndef FMGROIDS_H
#define FMGROIDS_H

/*
 *	Constant macros for the OIDs of entries in pg_proc.
 *
 *	NOTE: macros are named after the prosrc value, ie the actual C name
 *	of the implementing function, not the proname which may be overloaded.
 *	For example, we want to be able to assign different macro names to both
 *	char_text() and name_text() even though these both appear with proname
 *	'text'.  If the same C function appears in more than one pg_proc entry,
 *	its equivalent macro will be defined with the lowest OID among those
 *	entries.
 */
|;

print T
qq|/*-------------------------------------------------------------------------
 *
 * fmgrtab.c
 *    The function manager's table of internal functions.
 *
 * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * NOTES
 *
 *	******************************
 *	*** DO NOT EDIT THIS FILE! ***
 *	******************************
 *
 *	It has been GENERATED by $0
 *	from $infile[0]
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"
#include "knl/knl_variable.h"

#include "utils/fmgrtab.h"

|;

print B
qq|/*-------------------------------------------------------------------------
 *
 * pg_builtin_proc.h
 *    Macros that define the declares of built-in functions.
 *
 * NOTES
 *	******************************
 *	*** DO NOT EDIT THIS FILE! ***
 *	******************************
 *
 *	It has been GENERATED by $0
 *	from $infile[0]
 *
 *-------------------------------------------------------------------------
 */
|;

# Emit #define's and extern's -- only one per prosrc value
my %seenit;
foreach my $s (sort { $a->{proname} cmp $b->{proname} } @fmgr)
{
	next if $seenit{ $s->{prosrc} };
	$seenit{ $s->{prosrc} } = 1;
	print H "#define F_" . uc $s->{prosrc} . " $s->{oid}\n";
	print T "extern Datum $s->{prosrc} (PG_FUNCTION_ARGS);\n";
	print B "extern Datum $s->{prosrc} (PG_FUNCTION_ARGS);\n";
}

# Create the fmgr_builtins table
print T "\nconst FmgrBuiltin fmgr_builtins[] = {\n";
foreach my $s (sort { $a->{prosrc} cmp $b->{prosrc} } @fmgr)
{
	print T
"  { $s->{oid}, \"$s->{prosrc}\", $s->{nargs}, $s->{strict}, $s->{retset}, $s->{prosrc}, $s->{prorettype} },\n";
	$nfmgrfuncs = $nfmgrfuncs + 1;
}

print H "\n#define nBuiltinFuncs  $nfuncs\n";
print H "\n#define NFMGRFUNCS  $nfmgrfuncs\n";


# And add the file footers.
print H "\n#endif /* FMGROIDS_H */\n";

print T
qq|  /* dummy entry is easier than getting rid of comma after last real one */
  /* (not that there has ever been anything wrong with *having* a
     comma after the last field in an array initializer) */
  { 0, NULL, 0, false, false, NULL, InvalidOid}
};

/* Note fmgr_nbuiltins excludes the dummy entry */
const int fmgr_nbuiltins = (sizeof(fmgr_builtins) / sizeof(FmgrBuiltin)) - 1;
|;

close(H);
close(T);
close(B);

# Finally, rename the completed files into place.
RenameTempFile($oidsfile, $tmpext);
RenameTempFile($tabfile,  $tmpext);
RenameTempFile($builtinfile,  $tmpext);

sub RenameTempFile
{
	my $final_name = shift;
	my $extension  = shift;
	my $temp_name  = $final_name . $extension;
	print "Writing $final_name\n";
	rename($temp_name, $final_name) || die "rename: $temp_name: $!";
}

sub usage
{
	die <<EOM;
Usage: perl -I [directory of Catalog.pm] Gen_fmgrtab.pl [path to pg_proc.h]

Gen_fmgrtab.pl generates fmgroids.h and fmgrtab.c from pg_builtin_proc.cpp

Report bugs to <pgsql-bugs\@postgresql.org>.
EOM
}

exit 0;
